import { scheduleMicroTask } from '../../react-dom/src/hostConfig';
import { beginWork } from './beginWork';
import {
	commitHookEffectListCreate,
	commitHookEffectListDestroy,
	commitHookEffectListUnmount,
	commitLayoutEffects,
	commitMutationEffects
} from './commitWork';
import { completeWork } from './completeWork';
import {
	createWorkInProgress,
	FiberNode,
	FiberRootNode,
	PendingPassiveEffects
} from './fiber';
import {
	HostEffectMask,
	MutationMask,
	NoFlags,
	PassiveMask
} from './fiberFlags';
import {
	getHighestPriorityLane,
	getNextLane,
	Lane,
	lanesToSchedulerPriority,
	markRootFinished,
	markRootSuspended,
	mergeLanes,
	NoLane,
	SyncLane
} from './fiberLanes';
import { flushSyncCallbacks, scheduleSyncCallback } from './syncTaskQueue';
import { HostRoot } from './workTags';
import {
	unstable_scheduleCallback as scheduleCallback,
	unstable_NormalPriority as NormalPriority,
	unstable_shouldYield,
	unstable_cancelCallback
} from 'scheduler';
import { HookHasEffect, Passive } from './hookEffectTags';
import { throwException } from './fiberThrow';
import { SuspenseException, getSuspenseThenable } from './thenable';
import { unwindWork } from './fiberUnwindWork';
import { resetHooksOnUnwind } from './fiberHooks';

// ！！！！！重要
// 去duyi new-react 项目里面看调度器 源码 里面有笔记 也有自己的大概实现
// unstable_scheduleCallback去看一遍 这个函数的实现

// scheduler 调度器中的优先级
// 数字越小 优先级越高
// ImmediatePriority  代表同步更新  用户点击事件 键盘事件 1
// UserBlockingPriority  代表用户滚动事件等 2
// NormalPriority 正常的优先级 其他默认 事件3
// LowPriority  低优先级 4
// IdlePriority 空闲时的优先级 5

let workInProgress: FiberNode | null = null;
let wipRootRenderLane: Lane = NoLane; // 当前触发更新的 lane
let rootDoesHasPassiveEffects = false; // 表示是否已经调调度了 副作用 避免多次重复的调度副作用呢

type RootExitStatus = number;

// 工作中的状态
const RootInProgress = 0;

// 并发中间状态
const RootInComplete = 1;

// 完成状态
const RootCompleted = 2;

// 未完成状态，不用进入commit阶段
const RootDidNotComplete = 3;

let workInProgressRootExitStatus: number = RootInProgress;

// Suspense
type SuspendedReason =
	| typeof NotSuspended
	| typeof SuspendedOnError
	| typeof SuspendedOnData
	| typeof SuspendedOnDeprecatedThrowPromise;
const NotSuspended = 0;
const SuspendedOnError = 1;
const SuspendedOnData = 2;
const SuspendedOnDeprecatedThrowPromise = 4;

let workInProgressSuspendedReason: SuspendedReason = NotSuspended;
let workInProgressThrownValue: any = null;

// TODO 执行过程中报错了

// 初始化
function prepareFreshStack(root: FiberRootNode, lane: Lane) {
	root.finishedLane = NoLane;
	root.finishedWork = null;
	// 创建 一个 workInProgress 当前正在工作中的 fiber
	// 如果是一个任务正在执行 被另一个任务打断 插入了执行 那么 alternate 是有值的 workInProgress 就是之前打断时的 处理了一部分的fiber 在此基础上 再执行
	workInProgress = createWorkInProgress(root.current, {});
	wipRootRenderLane = lane;

	workInProgressRootExitStatus = RootInProgress;
	workInProgressSuspendedReason = NotSuspended;
	workInProgressThrownValue = null;
}

// loop 方法是 render 阶段的入口
// 协调器 更新时候的 入口
// 在 Fiber 中 协调更新  root.render(app) =>  updateContainer 会调用 该方法
//  作用是 协调 传入过来的 Fiber  生成 子fiber 标记 副作用 等
// Fiber 中有 updateQueue
// 情况1:这里 可能是  root.render(app) =>  updateContainer 调用传过来的 根Fiber
// 情况2:也有可能是 setState dispatch 调用 传过来的 当前节点的Fiber
// 所以第一步 如果 情况2的话 要一直 递归 往上找 retune 找 父根fiber  为止 然后 从 根Fiber 开始 更新协调

// 结论 1.在react 里面 本来lane对应的优先级是 SyncLane同步 微任务队列调度的任务   就不会用用 scheduler 调度器去调度。全部加入微任务队列 不 可中断执行
// 对应事件：点击，键盘输入事件。虽然SyncLane对应 ImmediatePriority 但有个判断是 等于 SyncLane 就用微任务调度

// 2.其他优先级 对应 scheduler 调度器等 2 3 4 5 都用 scheduler调度去 异步宏任务调度。如果执行过程中 任务到期了 则 将任务 转成 不可中断的。
// 该代码中对应事件：首次渲染，useEffect的回调执行,滚动 拖拽等事件。
// 实际上 useEffect 好像是在 commit阶段结束后 同步执行的？？？？？

export function scheduleUpdateOnFiber(fiber: FiberNode, lane: Lane) {
	// scheduleUpdateOnFiber该方法 由很多地方 触发更新

	// lane 表示 触发该次更新的优先级
	// fiberRootNode
	// 找 root 然后从 root 开始
	// 根Fiber 的 stateNode 为 root
	const root = markUpdateLaneFromFiberToRoot(fiber, lane);

	//当触发了更新就将 update 的lane记录
	markRootUpdated(root, lane); // 将 该lane优先级记录到 root中去 也就是将 lane merge到pendingLanes 中去表示 该lane还未被调度更新

	// 后面交给 调度器 从中选取哪个 lane去 协调更新
	ensureRootIsScheduled(root);
}

// schedule阶段入口
// 保持 root 被调度
// 目标 1.实现「某些判断机制」，选出一个lane
// 2.实现类似防抖、节流的效果，合并宏/微任务中触发的更新
// 从 root.pendingLanes 中调一个合适的 lane 出来进行协调更新
// 注意：ensureRootIsScheduled函数可能由多个地方调用
// 1.用户操作触发了更新。可能插入了一个更高优先级任务或低优先级任务
// 2.当前正在执行的更新任务  执行中断或者顺利执行完了 都会重新执行 ensureRootIsScheduled 用来向调取最小堆中插入任务 或者 重置 然后返回
export function ensureRootIsScheduled(root: FiberRootNode) {
	// 从 root.pendingLanes 中调一个合适的 lane 出来进行协调更新
	// 取出优先级 最高的lane
	// 注意取出来的 updateLane 始终是优先级最高的
	const updateLane = getNextLane(root);

	const existingCallback = root.callbackNode; // 获取当前正在调度的任务
	// 为什么能确定是当前正在调度的任务？因为updateLane是 react任务lanes厉害中 优先级最高的

	if (updateLane === NoLane) {
		// 如果没有 lane  就代表 没有 update需要消费 就代表没有更新
		// 因为每次调用更新的时候都会 将lane 合并到 root.pendingLanes中

		if (existingCallback !== null) {
			// root的pendingLanes里的更新都被消费了 那么 调度器里的最小堆里也不应该有任务了

			// 因为 root的pendingLanes 集合里面 的lane都被消费了 所以肯定没有更新需要消费了 所以取消掉 当前任务的回调
			// 该函数表示传入一个 任务 将任务的 task.callBack 置为null 置为null 间接的就 从 最小堆中删除了
			// 因为 调度器取任务的执行的时候 判断task.callBack 为null 就跳过取下一个任务
			unstable_cancelCallback(existingCallback);
		}

		// 不用进行调度
		root.callbackNode = null;
		root.callbackPriority = NoLane;
		return;
	}

	const curPriority = updateLane; // 此次更新 的优先级

	const prevPriority = root.callbackPriority; // 上次更新的优先级
	// curPriority 优先级一定是 react.pendingLanes 集合 中 优先级最高的
	if (curPriority === prevPriority) {
		// 如果当前任务优先级 和 上一次任务优先级一样

		// 可能1:用户又拆入了一个任务 是也会执行ensureRootIsScheduled 当前任务和正在执行的任务优先级一样时且当前优先级还是最高的 因为updateLane取出来 优先级永远是最高的 也直接return
		// 当前正在执行的任务执行完commit阶段后 会继续执行ensureRootIsScheduled 从root.pendingLanes里面取 出新插入的任务执行。所以不用担心 新插入任务得不到插入机会

		// 如果上次更新的优先级和这次的一样 表示上一次是中断的 没有执行完 此优先级的任务 还在 调度器的队列里面 不用再插入了
		//因为多次优先级一样的 更新 react 会进行批处理 会直接从 update 中 保存的 state循环链表中 去除多次的状态然后 执行更新

		// 最好将root.pendingLanes 对应的优先级位设置成已消费的 这样多个优先级一个的更新就会 合并？？？？？
		return;
	}

	if (existingCallback !== null) {
		// 走到这是用户操作 执行了ensureRootIsScheduled
		// 然后updateLane 的优先级比真正执行任务的优先级高
		// 所以取消掉 真正执行的任务 然后将 更高优先级的任务 插入到 调度器的最小堆中
		unstable_cancelCallback(existingCallback);
	}

	let newCallbackNode = null;

	if (true) {
		console.log(
			`在${updateLane === SyncLane ? '微' : '宏'}任务中调度，优先级：`,
			updateLane
		);
	}

	// react 框架是重运行时的框架 需要很多优先级 机制
	//  react 采用将 除了 同步优先级的lan外的 更新协调 分成 若干个小的宏任务 放到宏任务队列 每个宏任务之间可以中断 把主线交给 渲染流水线去绘制 这样就不会卡顿
	// 因为react 需要从 根节点开始 diff 这样会比较耗时 所以才用这种模式。
	// 为什么不采用 微任务 形式 然后还给浏览器呢?因为需要清空微任务队列 所以分割成 若干个微任务 也是需要全部执行完 才能去执行渲染流水线的

	// vue 和 框架  只会进行 微任务调度 因为 vue 知道 哪个具体的组件更新了 只需要diff那一个组件就行了 所以采用 微任务 调度

	// react 中分为同步更新和并发更新
	// 并发更新 采用 scheduler 调度器的 宏任务调度
	// 同步更新采用微任务调度
	if (updateLane === SyncLane) {
		// 同步优先级 用微任务调度
		// [performSyncWorkOnRoot, performSyncWorkOnRoot, performSyncWorkOnRoot]

		// SyncLane 同步任务优先级最高 当root.pendingLanes 中存在 同步任务时 就把该任务对应performSyncWorkOnRoot 拆入到微队列
		// 然后等主线程空闲 再去微任务中执行。

		// @ts-ignore
		// 实现类似防抖、节流的效果，合并微任务中 触发的更新
		// 调度 同步更新任务
		// 可以理解为将  多个 地方 触发了 scheduleUpdateOnFiber 更新
		// 不急着去 执行更新 而是将 renderRoot 函数 插入 一个队列中 合并的去执行
		// 点击事件里面 连续3个 setState(1) setState(2) setState(3) 所以在三个setState 后获取this.sate 是没有更新的 这里代码是在 js主线程里执行的
		// 当 三个setState都执行完了后 主线程里面没有任务执行了 就好 执行微队列里面的函数 也就是flushSyncCallbacks 函数
		//每执行一个setState就会将对应的 更新函数加入到队列
		// 将 root 和updateLane 提前bind 进去 当performSyncWorkOnRoot执行的时候就能 获取到
		//调度器将 同步微任务的 render 函数
		scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root, updateLane));
		// scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
		// flushSyncCallbacks函数 就是 遍历同步队列 执行里面的函数

		// scheduleMicroTask 就是然后将 该函数 插入到 微任务里面
		// 这里会执行多次 但不会会遍历多次syncQueue 同步任务队列 因为 有isFlushingSyncQueue全局遍历控制了 开始执行了一次 后面就不会遍历了
		// 当 主线程空闲时 就会从微任务队列里面取任务执行 但相同lane的任务只会执行一次 然后从 update链表中 获取多次更新的状态
		// 也就是说 多次 dispatch 只会触发一次更新渲染
		// 当第一个 更新 的 commit阶段结束后 会将 root.pendingLanes 里面的同步任务的lane 位优先级置为0 表示同步任务已经被消费了
		// 然后 再从 微任务队列里取 performSyncWorkOnRoot 执行的时候 performSyncWorkOnRoot函数里面会判断 当前lane 是不是已经 被消费了 被消费了 那就 不执行了 return；
		scheduleMicroTask(flushSyncCallbacks);

		// 如果 不这样进行合并放到微任务里面执行的话  第二个setState(2) 需要等第一个 setState(1)执行完 触发了 页面的更新后 才会执行setState(2) 会非常慢 因为会整个diff一遍
	} else {
		// 其他优先级 用宏任务调度

		// 根据updateLane不同的优先级 获取调度器对应的优先级
		const schedulerPriority = lanesToSchedulerPriority(updateLane);

		// 大概原理就是：

		// schedule 会将scheduleCallback每次传入的回调函数 包装成任务 根据优先级 插入到 最小堆  队列中 并返回该任务
		// 然后 利用postMessage  触发一个宏任务
		// 宏任务里面会开启一个循环 每次循环都会去取最小堆里面优先级任务高的任务执行 执行之前会判断 是否执行超过了5ms 超过了则 返回 最小堆中是否还有任务
		// 没有超过的话就执行 任务对应的回调。回调里面也会利用 调度器包提供的函数 判断当前是否执行超过了5ms 超过了则中断执行 返回一个 新函数  没有超过则一直执行 直到返回null
		// 调度器 判断任务的回调函数返回的是什么是函数的话将返回的函数作为任务的新回调函数。不是函数的话将 如果该任务 还是最小堆中优先级最高的 那就该任务从堆队列中删除， 表示该任务已经执行完了。
		// 还有任务的 话就开启下一个宏任务等待浏览器渲染后 再去从浏览器宏任务队列中取宏任务执行

		//scheduleCallback 传入的回调函数 就是一个  render阶段的render 函数 里面会依次处理Fiber 每遍历一个Fiber的时候就去判断执行时间是否超过了5ms 超过了则中断
		// 下次再 从上次处理的fiber 开始执行。

		// @ts-ignore
		newCallbackNode = scheduleCallback(
			schedulerPriority,
			// @ts-ignore
			performConcurrentWorkOnRoot.bind(null, root)
		);
	}

	root.callbackNode = newCallbackNode;
	root.callbackPriority = curPriority;
}

export function markRootUpdated(root: FiberRootNode, lane: Lane) {
	root.pendingLanes = mergeLanes(root.pendingLanes, lane);
}

export function markUpdateLaneFromFiberToRoot(fiber: FiberNode, lane: Lane) {
	let node = fiber;
	let parent = node.return;
	while (parent !== null) {
		parent.childLanes = mergeLanes(parent.childLanes, lane);
		const alternate = parent.alternate;
		if (alternate !== null) {
			alternate.childLanes = mergeLanes(alternate.childLanes, lane);
		}

		node = parent;
		parent = node.return;
	}
	if (node.tag === HostRoot) {
		return node.stateNode;
	}
	return null;
}

// 该函数是 并发更新的renderRoot 放到宏任务队列里面去执行的 是可以中断的
// 这里是render阶段的 入口
// didTimeout 当前 任务是否过期 didTimeout 这个参数是调度器 传入过来的
function performConcurrentWorkOnRoot(
	root: FiberRootNode,
	didTimeout: boolean
): any {
	// useEffect回调  也是通过调度器插入 最小堆队列中 调度执行的 且优先级为3
	// 可能当前宏任务 正在执行 useEffect回调任务时 用户操作插入了一个优先级为2的任务
	// 然后该任务得到了执行，执行到这 的时候 要判断下 useEffect的回调执行完毕了没 因为useEffect 回调里面可能会 触发优先级更高的 更新

	// 所以需要取执行一下flushPassiveEffects
	// 返回true代表 还有 回调没执行完 然后现在把他执行完了
	const curCallback = root.callbackNode;
	// 这里体现了一个原则， 在每轮commit前会把上一轮commit调度的useEffect回调执行
	const didFlushPassiveEffect = flushPassiveEffects(root.pendingPassiveEffects);
	if (didFlushPassiveEffect) {
		// 返回true 说明 有 useEffect回调执行
		// 执行完  所有的回调之后 判断 useEffect 里面有没有插入更高优先级的任务
		// root.callbackNode !== curCallback 表示插入了更高优先级的任务 因为执行了 scheduleCallback root.callbackNode  值也跟着变了

		if (root.callbackNode !== curCallback) {
			// 插入了优先级更高的任务 那就直接 return 让调度器把该任务从最小堆中删除 该任务对应的回调 从最小堆中删除了 但root.pendingLanes对应的 优先级 还为被标记为已更新 。

			// 然后调度器转头去执行 插入的优先级更高的任务

			// 当当前插入的优先级更高的任务顺利执行 完 commit阶段后 会重新执行ensureRootIsScheduled 去root.pendingLanes取相应未被消费的 更新插入到 最小堆中
			return null;
		}
	}
	// 获取优先级最高的lane
	const lane = getNextLane(root); // 这里获取的 lane 和 ensureRootIsScheduled  里面获取的是一样的 因为对应的最高优先级的 还未被 标记为 已消费 还是未消费状态 1

	const curCallbackNode = root.callbackNode; // 获取当前回调函数 对应的任务

	if (lane === NoLane) {
		// 这里表示 root.pendingLanes 已经没有更新了  对应需要消费的更新了 所以 直接返回
		// 这里是一直防御性编程
		return null;
	}

	// 判断 当前任务 需不需 可以中断的执行
	// didTimeout  为True 表示当前任务已经过期了 过期的任务 需要一口气执行完不能中断
	const needSync = lane === SyncLane || didTimeout; //如果是 同步1 的优先级 或者 任务到期了 那就 设置更新为不可中断的同步更新 阻塞
	// 一个任务本身就是 优先级同步的 或者是 过期了的 那这个任务就不会去中断执行
	// render阶段
	const exitStatus = renderRoot(root, lane, !needSync); //true 或false都有可能

	// 根据exitStatus 判断 任务是 执行完了 还是被中断执行的

	// render阶段 中断 了 或者执行完了

	switch (exitStatus) {
		// 并发异步执行被中断了
		case RootInComplete:
			// 时间切片不够了 中断 了 此时需要 进行判断 做出2种操作
			// 操作1:判断 在当前宏任务进行异步执行的时候 用户有没有操作触发优先级更高的回调 进而执行ensureRootIsScheduled  有 的话
			// 则表明 root.callbackNode  被重新赋值了 高优先级任务已经被插入了 最小堆。
			if (root.callbackNode !== curCallbackNode) {
				// 所以此时就得返回null 中断当前任务的执行。返回null 则告诉 调度器 去判断 当前任务是不是最小堆中 优先级最高的任务了 此时明显不是应该插入了 更高的
				// 所以调度器就将该任务从最小堆中 移除。等插入的优先级更高的任务顺利的执行完 commit阶段后 重新执行ensureRootIsScheduled  将该任务重新插入最小堆中
				return null;
			}
			// 操作2: 当前 root.callbackNode  没有变 则表明 root.pendingLens 中没有插入优先级更高的任务 则返回一个函数 让调度器 别从最小堆中删除该任务 继续调度 该任务。

			// 因为 最小 堆队列 任务中还有任务 调度器会重新向 浏览器宏任务队列中 推入一个宏任务去循环取最小 堆队列里的任务执行 看调度器源码！！

			// 此时直接返回了 是没有进行 commitRoot阶段的 所以浏览器看不到 有关这次更新相关的页面渲染更新？？只有当更新完成了 执行了commit阶段 才能看到？？？？！！！是的
			return performConcurrentWorkOnRoot.bind(null, root);

		// 执行完了 则进行commit阶段
		case RootCompleted:
			const finishedWork = root.current.alternate; // 取出缓存区的 fiber树 去commit
			root.finishedWork = finishedWork;
			root.finishedLane = lane;
			wipRootRenderLane = NoLane;
			commitRoot(root);
			break;

		// 未完成状态，不用进入commit阶段
		case RootDidNotComplete:
			markRootSuspended(root, lane);
			wipRootRenderLane = NoLane;

			ensureRootIsScheduled(root);
			break;

		default:
			if (true) {
				console.error('还未实现的并发更新结束状态');
			}
	}
}

// 该函数是 同步更新的renderRoot 放到微任务队列里面去执行的 是 不可中断的
function performSyncWorkOnRoot(root: FiberRootNode) {
	const nextLane = getNextLane(root); // 再去获取 一下 当前 合适 应该去更新update 的lane

	if (nextLane !== SyncLane) {
		// 走到这里 有两种情况
		// 1 当前nextLane 不是 SyncLane 说明是 不是同步更新 就不该走这个方法 需要 重新去调度
		// 2nextLane 是 NoLane  说明 root.pendingLanes 里面没有同步的更新任务 了 同步更新任务 已经被消费了 直接返回就行了
		// 这样 就达成了 多个 同步任务  调度器虽然将多个同步任务插入到 微任务队列里面去了
		//  但 这里 root上的 pendingLanes 代表同步任务的 位优先级已经被消费了 后面的 就直接return了 就不会再执行了
		// 这样就达成了 将多个同步任务 合并到一起的 效果
		// 然后 消费fiber  中 update 的时候 因为 update 上是以链表的形式存着 多次同步更新 的状态 依次将 action 取出来 执行就行了
		// 这样就能 实现 一次 消费 就能 获取 多个更新的状态
		ensureRootIsScheduled(root); // 再去调度 看pendingLanes 还有没有 没有被消费的  如果 nextLane 是 NoLane 就直接返回 了
		return;
	}

	const exitStatus = renderRoot(root, nextLane, false);

	switch (exitStatus) {
		case RootCompleted:
			const finishedWork = root.current.alternate;
			root.finishedWork = finishedWork;
			root.finishedLane = nextLane;
			wipRootRenderLane = NoLane;
			commitRoot(root);
			break;
		case RootDidNotComplete:
			wipRootRenderLane = NoLane;
			markRootSuspended(root, nextLane);
			ensureRootIsScheduled(root);
			break;
		default:
			if (true) {
				console.error('还未实现的同步更新结束状态');
			}
			break;
	}
}

let c = 0;

// renderRoot 开启render阶段
// shouldTimeSlice是否 应该进行可中断
// lane 这次更新对应的 优先级
function renderRoot(root: FiberRootNode, lane: Lane, shouldTimeSlice: boolean) {
	if (true) {
		console.log(`开始${shouldTimeSlice ? '并发' : '同步'}更新`, root);
	}

	if (wipRootRenderLane !== lane) {
		// 因为renderRoot 可能是 中断更新后 继续执行的
		// 所以当 触发更新的lane 和当前lane 是相等的 就是中断的 就不用重新初始化了 因为wipRootRenderLane 没有被重置

		// 不相等 即wipRootRenderLane 还是 NoLane的时候
		//  或者  因为插入了其他优先级更高的 任务 此时 wipRootRenderLane 还是上次的lane 和这次不相等了 那就要重新 初始化
		prepareFreshStack(root, lane);
	}

	do {
		try {
			if (
				workInProgressSuspendedReason !== NotSuspended &&
				workInProgress !== null
			) {
				const thrownValue = workInProgressThrownValue;

				workInProgressSuspendedReason = NotSuspended;
				workInProgressThrownValue = null;

				throwAndUnwindWorkLoop(root, workInProgress, thrownValue, lane);
			}
			// 根据 是否可以中断执行 执行 非阻塞 render  和 阻塞render阶段
			shouldTimeSlice ? workLoopConcurrent() : workLoopSync();
			break;
		} catch (e) {
			if (true) {
				console.warn('workLoop发生错误', e);
			}
			c++;
			if (c > 20) {
				break;
				console.warn('break!');
			}
			handleThrow(root, e);
		}
	} while (true);

	if (workInProgressRootExitStatus !== RootInProgress) {
		return workInProgressRootExitStatus;
	}

	// 中断执行的
	if (shouldTimeSlice && workInProgress !== null) {
		// 表示 应该实现时间切片 且 还有 fiber没处理完 所以是中断执行的 返回1
		// 表示 中断了 此时 workInProgress没处理完
		// 返回 并发中间状态 1
		return RootInComplete;
	}

	// render阶段执行完
	if (!shouldTimeSlice && workInProgress !== null && true) {
		console.error(`render阶段结束时wip不应该不是null`);
	}

	// 完整 的执行完了 返回2
	return RootCompleted;
}

// commitRoot 方法是 commit阶段的 入口
function commitRoot(root: FiberRootNode) {
	const finishedWork = root.finishedWork;

	if (finishedWork === null) {
		return;
	}

	if (true) {
		console.warn('commit阶段开始', finishedWork);
	}
	const lane = root.finishedLane;

	if (lane === NoLane && true) {
		console.error('commit阶段finishedLane不应该是NoLane');
	}
	// commitRoot 主要实现 Flags 对应的操作 主要实现 Placement 、Update 、Deletion Ref Visibility 5个操作
	// 实现 root 的flags  和 subtreeFlags
	//  如果有副作用操作 就 主要实现 三个阶段  没有的话 也要切换一下树
	// 1 beforeMutation阶段

	//2 mutation阶段   突变阶段 dom属性由一个值 突变到另一个值

	// 中间会fiber树的切换  树的切换 就是 root的current 指向最新的 fiber树继续了

	//3  layout阶段 useLayoutEffect 会在该阶段执行

	// 重置
	root.finishedWork = null;
	root.finishedLane = NoLane;

	// 将 root.pendingLanes 中 代表 lane 的优先级 置为0 表示 该优先级已经被消费了
	// 后续再执行 再从微任务中 取performSyncWorkOnRoot 执行就不会执行了 因为performSyncWorkOnRoot里面首先会判断 当前lane 是不是已经被消费了 被消费了 那就 不执行了 return；
	//  如果后面 再次出发 scheduleUpdateOnFiber 的话 pendingLanes 会再次被设置成 未消费的
	markRootFinished(root, lane);

	if (
		(finishedWork.flags & PassiveMask) !== NoFlags ||
		(finishedWork.subtreeFlags & PassiveMask) !== NoFlags
	) {
		if (!rootDoesHasPassiveEffects) {
			rootDoesHasPassiveEffects = true;
			// 调度副作用
			// 将 副作用的函数 调度到宏任务中执行
			scheduleCallback(NormalPriority, () => {
				// 执行副作用
				flushPassiveEffects(root.pendingPassiveEffects);
				return; // 这个 return 是有作用的 返回null表示  这个函数是顺利执行完的 scheduler 内部会根据一定条件将该任务从最小堆中移除
			});
		}
	}

	// 判断是否存在3个子阶段需要执行的操作
	// root flags root subtreeFlags
	// 有的话 就执行 3个阶段的操作

	// 表示root的子节点树下面 有 MutationMask 和 PassiveMask 对应的 副作用操作
	const subtreeHasEffect =
		(finishedWork.subtreeFlags & (MutationMask | PassiveMask)) !== NoFlags;

	// 表示root的子节点下面 有 MutationMask 和 PassiveMask 对应的 副作用操作
	const rootHasEffect =
		(finishedWork.flags & (MutationMask | PassiveMask)) !== NoFlags;

	// MutationMask：Placement | Update | ChildDeletion | Ref | Visibility这些操作
	// rootHasEffect：有fiber节点 需要执行 useEffect 的 副作用回调函数 或者卸载的回调函数

	if (subtreeHasEffect || rootHasEffect) {
		// beforeMutation
		// mutation Placement

		// 该方法 的作用 就是 因为 明确了  子Fiber树里面 有 MutationMask PassiveMask副作用 操作 或者 当前fiber上有 MutationMask PassiveMask副作用操作
		// 那就找到 具体哪个fiber 上有对应的副作用 然后  对应的fiber 上的 dom 执行 副作用操作

		// 首次渲染的 时候 唯一的副作用 存在 finishedWork.child 的 flags 上 也就是  app 节点 因为app节点要插入到 root上
		// finishedWork.subtreeFlags 上也存在 因为冒泡到上面了
		commitMutationEffects(finishedWork, root);

		// 完成 树的切换
		root.current = finishedWork;

		// 阶段3/3:Layout
		commitLayoutEffects(finishedWork, root);
	} else {
		// 完成 树的切换
		// 将 当前工作的 fiber 树 赋值给 root.current
		root.current = finishedWork;
	}

	rootDoesHasPassiveEffects = false;
	ensureRootIsScheduled(root); // 当前 调度任务   顺利执行完了 重新调度一下将其他 待消费带lane插入到  调度器的最小堆中
}

function flushPassiveEffects(pendingPassiveEffects: PendingPassiveEffects) {
	let didFlushPassiveEffect = false;

	//这个函数表示 先把那些函数组件 已经被标记为删除的 函数组件里面的 useEffect的 销毁函数先执行一遍
	pendingPassiveEffects.unmount.forEach((effect) => {
		didFlushPassiveEffect = true;
		// 传入Passive 表示 只执行 tag 为 Passive 的Effect
		// 既然是要卸载的函数组件 所以effect的tag肯定 Passive
		commitHookEffectListUnmount(Passive, effect);
	});
	pendingPassiveEffects.unmount = []; // 清空

	// 这个函数表示 将 	pendingPassiveEffects.update 里面所以 标记为Passive | HookHasEffect (:即该Effect 需要执行副作用)的effect的 destroy 先执行一遍
	pendingPassiveEffects.update.forEach((effect) => {
		didFlushPassiveEffect = true;
		commitHookEffectListDestroy(Passive | HookHasEffect, effect);
	});

	// 这个函数表示 将 	pendingPassiveEffects.update 里面所以 标记为Passive | HookHasEffect (:即该Effect 需要执行副作用)的effect的 create 先执行一遍
	// 然后执行create 的函数结果 赋值给 effect.destroy
	pendingPassiveEffects.update.forEach((effect) => {
		didFlushPassiveEffect = true;
		commitHookEffectListCreate(Passive | HookHasEffect, effect);
	});

	pendingPassiveEffects.update = [];

	// 因为useEffect 的副作用函数里面 也可能会出发 update 也就是 里面可能会执行 dispatch 因为 update 此时的上下问是 同步优先级的 所以 ensureRootIsScheduled 就会将 对应的任务放到 syncQueue 里面
	// 然后等所有 useEffect函数都执行完后  直接调用 flushSyncCallbacks 同步去 执行 而不是 scheduleMicroTask(flushSyncCallbacks);这样加入微任务执行
	flushSyncCallbacks();
	// const app = () => {
	// 	console.log(11, 'log');
	// 	const [a, setA] = useState(1);
	// 	useEffect(() => {
	// 		setA(2);
	// 		console.log(a, 'log'); //闭包影响
	// 		Promise.resolve().then(() => {
	// 			console.log(4444, 'log');
	// 		});
	// 		// 这里a 还是1 因为 //闭包影响 useEffect的副作用更新是 异步的setA(2) 触发的update 更新加入到了 syncQueue任务队列 。需要等 所有的useEffect
	// 		// 的副作用函数 都执行完后 才会 执行 flushSyncCallbacks 也就是去执行 syncQueue任务队列！！！
	//  当 执行setA(2)的时候 先 输出 11 然后输出 4444 因为 再useEffect 里面触发的 更新是 同步优先级的  等所有 useEffect函数都执行完后  直接调用 flushSyncCallbacks 同步去 执行 而不是 scheduleMicroTask(flushSyncCallbacks);这样加入微任务执行
	// 所以 先输出 11 再输出 444
	// 	});
	// };
	return didFlushPassiveEffect;
}

// 同步 不可中断 的 执行 render阶段
function workLoopSync() {
	while (workInProgress !== null) {
		performUnitOfWork(workInProgress);
	}
}

// 这个 递 阶段 和归阶段 结合 duyi的 图看

// 总的一个出来逻辑就是
// 1.根据 走当前Fiber 的beginWork 如果 有 子child 则继续子child 的beginWork 一直深度遍历
// 2.没有子child了 则 走当前wip的 completeWork
// 3.然后 看是否有兄弟节点 有兄弟节点则 走兄弟节点的 beginWork 继续深度遍历
// 4.没有兄弟节点了 则返回上一层 走 父fiber的 completeWork  然后 重复 步骤3.

// 非阻塞可中断 的 执行 render阶段
function workLoopConcurrent() {
	while (workInProgress !== null && !unstable_shouldYield()) {
		performUnitOfWork(workInProgress);
	}
}

function performUnitOfWork(fiber: FiberNode) {
	const next = beginWork(fiber, wipRootRenderLane);

	fiber.memoizedProps = fiber.pendingProps;

	if (next === null) {
		completeUnitOfWork(fiber);
	} else {
		workInProgress = next;
	}
}

function completeUnitOfWork(fiber: FiberNode) {
	let node: FiberNode | null = fiber;

	do {
		completeWork(node);
		const sibling = node.sibling;

		if (sibling !== null) {
			workInProgress = sibling;
			return;
		}
		node = node.return; // 当 node为hostRootFiber 的时候 他的 turn 为null
		// 此时 workInProgress 为 null  然后 跳出循环 进入 commit阶段
		workInProgress = node;
	} while (node !== null);
}

function handleThrow(root: FiberRootNode, thrownValue: any): void {
	/*
		throw可能的情况
			1. use thenable
			2. error (Error Boundary处理)
	*/
	if (thrownValue === SuspenseException) {
		workInProgressSuspendedReason = SuspendedOnData;
		thrownValue = getSuspenseThenable();
	} else {
		const isWakeable =
			thrownValue !== null &&
			typeof thrownValue === 'object' &&
			typeof thrownValue.then === 'function';

		workInProgressThrownValue = thrownValue;
		workInProgressSuspendedReason = isWakeable
			? SuspendedOnDeprecatedThrowPromise
			: SuspendedOnError;
	}
	workInProgressThrownValue = thrownValue;
}

function throwAndUnwindWorkLoop(
	root: FiberRootNode,
	unitOfWork: FiberNode,
	thrownValue: any,
	lane: Lane
) {
	// unwind前的重置hook，避免 hook0 use hook1 时 use造成中断，再恢复时前后hook对应不上
	resetHooksOnUnwind(unitOfWork);
	throwException(root, thrownValue, lane);
	unwindUnitOfWork(unitOfWork);
}

function unwindUnitOfWork(unitOfWork: FiberNode) {
	let incompleteWork: FiberNode | null = unitOfWork;
	do {
		const next = unwindWork(incompleteWork);

		if (next !== null) {
			next.flags &= HostEffectMask;
			workInProgress = next;
			return;
		}

		const returnFiber = incompleteWork.return as FiberNode;
		if (returnFiber !== null) {
			returnFiber.deletions = null;
		}
		incompleteWork = returnFiber;
		// workInProgress = incompleteWork;
	} while (incompleteWork !== null);

	// 没有 边界 中止unwind流程，一直到root
	workInProgress = null;
	workInProgressRootExitStatus = RootDidNotComplete;
}
